package org.jsets.fastboot.oss.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.transaction.Transactional;
import org.jsets.fastboot.oss.mapper.StorageMapper;
import org.jsets.fastboot.oss.model.constant.StatusTypes;
import org.jsets.fastboot.oss.model.constant.Storages;
import org.jsets.fastboot.oss.model.dto.StorageQuery;
import org.jsets.fastboot.oss.model.entity.Storage;
import org.jsets.fastboot.oss.service.IStorageConfigService;
import org.jsets.fastboot.oss.service.IStorageEngineService;
import org.jsets.fastboot.persistent.support.LambdaQuerier;
import org.jsets.fastboot.persistent.support.LambdaUpdater;
import org.jsets.fastboot.util.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class StorageConfigService extends ServiceImpl<StorageMapper, Storage> implements IStorageConfigService {

    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
	private final Map<String, IStorageEngineService> engines = Maps.newHashMap();

	@Override
	public void initialize() {
		List<Storage> configs = this.selectList(new StorageQuery());
		for (Storage config : configs) {
			if(this.containEngineService(config.getId())) {
				continue;
			}
			try {
				IStorageEngineService engineService = this.createEngineService(config);
				this.putEngineServiceIfNecessary(config.getId(), engineService);
			} catch (Exception e) {
			    this.updateStatus(config.getId(), StatusTypes.DISABLE);
				log.warn("初始化存储引擎[" + config.getName() + "]失败", e);
			}
		}
	}
	
	
	@Override
	@Transactional
	public void insertStorage(Storage entity) {
		entity.setId(StringUtils.getUUID());
		IStorageEngineService engineService = this.createEngineService(entity);
		this.putEngineServiceIfNecessary(entity.getId(), engineService);
		this.save(entity);
	}

	@Override
	@Transactional
	public void deleteStorage(String id) {
		this.removeById(id);
		this.removeEngineServiceIfNecessary(id);
	}
	
	@Override
	@Transactional
	public void updateStorage(Storage entity) {
		this.removeEngineServiceIfNecessary(entity.getName());
		IStorageEngineService engineService = this.createEngineService(entity);
		this.putEngineServiceIfNecessary(entity.getId(), engineService);
		this.updateById(entity);
	}
	
	@Override
	public void updateStatus(String id, Integer status) {
		LambdaUpdater
			.build(Storage.class)
			.setBaseMapper(this.getBaseMapper())
			.set(Storage::getStatus, status)
			.eq(Storage::getId, id)
			.update();
	}
	
	@Override
	public IStorageEngineService getOrCreateEngineService(String id) {
		this.rwLock.readLock().lock();
		try {
			if (this.engines.containsKey(id)) {
				return this.engines.get(id);
			}
		} finally {
			this.rwLock.readLock().unlock();
		}
		
		
		this.rwLock.writeLock().lock();
		try {
			Storage config = this.getById(id);
			Objects.requireNonNull(config, "找不到存储引擎");
			IStorageEngineService engineService = this.createEngineService(config);
			Objects.requireNonNull(engineService, "初始化存储引擎["+config.getName()+"]失败");
			this.putEngineServiceIfNecessary(config.getId(), engineService);
			return engineService;
		} finally {
			this.rwLock.writeLock().unlock();
		}
	}

	@Override
	public boolean containEngineService(String id) {
		this.rwLock.readLock().lock();
		try {
			return this.engines.containsKey(id);
		} finally {
			this.rwLock.readLock().unlock();
		}
	}

	@Override
	public Storage getByName(String name) {
		return LambdaQuerier
				.build(Storage.class)
				.setBaseMapper(this.getBaseMapper())
				.eq(Storage::getName, name)
				.selectOne();
	}

	@Override
	public Page<Storage> selectPage(StorageQuery criteria) {
		return LambdaQuerier
				.build(Storage.class)
				.setBaseMapper(this.getBaseMapper())
				.setCurrentPage(criteria.getCurrent())
				.setPageSize(criteria.getSize())
				.likeIfNecessary(Storage::getName, criteria.getName())
				.eqIfNecessary(Storage::getType, criteria.getType())
				.selectPage();
	}

	@Override
	public List<Storage> selectList(StorageQuery criteria) {
		return LambdaQuerier
				.build(Storage.class)
				.setBaseMapper(this.getBaseMapper())
				.likeIfNecessary(Storage::getName, criteria.getName())
				.eqIfNecessary(Storage::getType, criteria.getType())
				.selectList();
	}

	private void putEngineServiceIfNecessary(String key, IStorageEngineService engineService) {
		this.rwLock.writeLock().lock();
        try {
        	if(!this.engines.containsKey(key)) {
				this.engines.put(key, engineService);
			};
        } finally {
        	this.rwLock.writeLock().unlock();
        }
	}
	
	private void removeEngineServiceIfNecessary(String key) {
		this.rwLock.writeLock().lock();
        try {
        	if(this.engines.containsKey(key)) {
				IStorageEngineService engineService = this.engines.get(key);
				engineService.close();
				this.engines.remove(key);
			};
        } finally {
        	this.rwLock.writeLock().unlock();
        }
	}
	
	private IStorageEngineService createEngineService(Storage config) {
		IStorageEngineService engineService = null;
		switch (String.valueOf(config.getType())) {
		case Storages.LOCAL_STR:
			log.info("OSS 存储引擎[LOCAL]");
			engineService = new LocalStorageService(config);
			break;
		case Storages.MINIO_STR:
			log.info("OSS 存储引擎[MINIO]");
			if(config.getCluster()) {
				engineService = new ClusterMinioStorageService(config);
			}else {
				//engineService = new MinioStorageService(config);
			}
			break;
		case Storages.QINIU_STR:
			log.info("OSS 存储引擎[QINIU]");
			engineService = new QiniuStorageService(config);
			break;
		case Storages.ALIYUN_STR:
			log.info("OSS 存储引擎[ALIYUN]");
			engineService = new AliyunStorageService(config);
			break;
		case Storages.TENCENT_STR:
			log.info("OSS 存储引擎[TENCENT]");
			engineService = new QiniuStorageService(config);
			break;
		default:
			break;
		}
		engineService.initialize();
		this.updateStatus(config.getId(), StatusTypes.OK);
		return engineService;
	}
	
}